package com.train.mgr.modules.biz.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.train.common.enums.ProjectTypeEnum;
import com.train.common.model.PageResult;
import com.train.mgr.modules.biz.dto.StatLessonGroupTraineeDTO;
import com.train.mgr.modules.biz.dto.request.StatQueryDTO;
import com.train.mgr.modules.biz.entity.Course;
import com.train.mgr.modules.biz.entity.StatLessonGroup;
import com.train.mgr.modules.biz.mapper.StatLessonGroupMapper;
import com.train.mgr.modules.biz.service.ICourseService;
import com.train.mgr.modules.biz.service.IStatLessonGroupService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * <p>
 * 课时学员分组统计表 服务实现类
 * </p>
 *
 * @author cxg
 * @date 2025-10-11
 */
@Slf4j
@RequiredArgsConstructor
@Service("IStatLessonGroupService")
public class StatLessonGroupServiceImpl extends ServiceImpl<StatLessonGroupMapper, StatLessonGroup> implements IStatLessonGroupService {

    private final ICourseService courseService;
    /**
     * 统计课时学习情况-按分组
     * @param params
     * @return
     */
    @Override
    public PageResult<StatLessonGroup> lessonGroupList(StatQueryDTO params){
        Page<StatLessonGroup> page = params.buildPage();
        LambdaQueryWrapper<StatLessonGroup> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(StatLessonGroup::getCourseId,params.getCourseId());
        queryWrapper.eq(params.getLessonId()!=null,StatLessonGroup::getLessonId,params.getLessonId());
        queryWrapper.eq(params.getGroupId()!=null && params.getGroupId() >0,StatLessonGroup::getGroupId,params.getGroupId());
        queryWrapper.orderByAsc(StatLessonGroup::getGroupCreateTime);
        Page<StatLessonGroup> resultPage = baseMapper.selectPage(page, queryWrapper);
        return PageResult.of(resultPage, StatLessonGroup.class);

    }

    @Override
    @Transactional
    public void buildLessonTraineeStat(String courseId) {
        List<StatLessonGroup> stats = new ArrayList<>();
        UpdateWrapper<StatLessonGroup> updateWrapper = new UpdateWrapper<>();
        updateWrapper.ne(StrUtil.isBlank(courseId),"id", 0L);
        updateWrapper.eq(StrUtil.isNotBlank(courseId),"course_id", courseId);
        baseMapper.delete(updateWrapper);
        List<Course> courseList;
        if (StrUtil.isBlank(courseId)){
            courseList = courseService.getCourseList();
        } else {
            Course course = courseService.getById(courseId);
            courseList = Collections.singletonList(course);
        }
        for (Course course : courseList) {
            List<StatLessonGroupTraineeDTO> groupTraineeDTOS = baseMapper.buildLessonTraineeStat(course.getId(), null);
            if (CollUtil.isEmpty(groupTraineeDTOS)) {
                continue;
            }

            // 按课时ID分组
            Map<Long, List<StatLessonGroupTraineeDTO>> lessons = groupTraineeDTOS.stream()
                    .collect(Collectors.groupingBy(StatLessonGroupTraineeDTO::getLessonId));

            for (Map.Entry<Long, List<StatLessonGroupTraineeDTO>> lessonEntry : lessons.entrySet()) {
                Long lessonId = lessonEntry.getKey();
                List<StatLessonGroupTraineeDTO> dtos = lessonEntry.getValue();

                // 按组ID分组
                Map<Long, List<StatLessonGroupTraineeDTO>> userGrouped = dtos.stream()
                        .collect(Collectors.groupingBy(StatLessonGroupTraineeDTO::getGroupId));

                for (Map.Entry<Long, List<StatLessonGroupTraineeDTO>> groups : userGrouped.entrySet()) {
                    List<StatLessonGroupTraineeDTO> groupDtos = groups.getValue();

                    // 筛选出话术跟读练习项目
                    List<StatLessonGroupTraineeDTO> readingProjects = groupDtos.stream()
                            .filter(x -> ProjectTypeEnum.READING.getCode().equals(x.getExerciseType()))
                            .collect(Collectors.toList());

                    String groupName = groupDtos.get(0).getGroupName();
                    Long groupId = groupDtos.get(0).getGroupId();
                    StatLessonGroup stat = new StatLessonGroup();
                    // 人数
                    int execriseNum = (int) readingProjects.stream().map(StatLessonGroupTraineeDTO::getUserId).distinct().count();
                    int readingCount = readingProjects.size();
                    int joinNum = (int) groupDtos.stream().map(StatLessonGroupTraineeDTO::getUserId).distinct().count();
                    stat.setGroupCreateTime(DateUtil.parse(groupDtos.get(0).getGroupCreateTime()));
                    // 基础信息设置
                    stat.setCourseId(course.getId());
                    stat.setLessonId(lessonId);
                    stat.setGroupName(groupName);
                    stat.setCustomerId(course.getCustomerId());
                    stat.setJoinNum(joinNum);
                    stat.setGroupId(groupId);
                    if (execriseNum == 0){
                        stat.setReadRepeatedTimes(0);
                    } else {
                        stat.setReadRepeatedTimes(BigDecimal.valueOf(readingCount).divide(BigDecimal.valueOf(execriseNum), 0, RoundingMode.HALF_UP).intValueExact());
                    }

                    //实际参与人数(有提交过练习的)，人均练习的统计除该值
                    long joinCount = groupDtos.stream().filter(x -> x.getPassingState() != null).map(StatLessonGroupTraineeDTO::getUserId).distinct().count();

                    if (joinCount == 0){
                        stat.setPerExercisesNum(0);
                        stat.setPerExercisesDuration(0);
                    } else {
                        // 人均练习次数
                        BigDecimal perExercisesNum = new BigDecimal(groupDtos.stream().filter(x -> x.getPassingState() != null).count())
                                .divide(new BigDecimal(joinCount), 0, RoundingMode.HALF_UP);
                        stat.setPerExercisesNum(perExercisesNum.intValue());

                        // 课时人均练习时长（分钟）
                        int totalTime = groupDtos.stream()
                                .map(StatLessonGroupTraineeDTO::getUseTime).filter(Objects::nonNull)
                                .reduce(0, Integer::sum);
                        BigDecimal durationInMinutes = new BigDecimal(totalTime)
                                .divide(new BigDecimal(joinCount), 2, RoundingMode.HALF_UP)
                                .divide(new BigDecimal(60), 0, RoundingMode.HALF_UP);
                        stat.setPerExercisesDuration(durationInMinutes.intValue());
                    }
                    // 完成情况统计
                    int finishNum = (int) groupDtos.stream()
                            .filter(x -> x.getIsFinish() != null && x.getIsFinish() == 1)
                            .map(StatLessonGroupTraineeDTO::getUserId)
                            .distinct()
                            .count();
                    stat.setFinishNum(finishNum);
                    // 完成情况统计
                    int noFinishNum = joinNum - finishNum;
                    stat.setNoFinishNum(noFinishNum);

                    // 课时完成率
                    if (joinNum == 0){
                        stat.setLessonFinishRate(BigDecimal.ZERO);
                    } else {
                        stat.setLessonFinishRate(
                                BigDecimal.valueOf(stat.getFinishNum())
                                        .divide(new BigDecimal(joinNum), 1, RoundingMode.HALF_UP)
                                        .multiply(BigDecimal.valueOf(100))
                        );
                    }

                    if (readingCount == 0) {
                        stat.setOverallAvgScore(BigDecimal.ZERO);
                        stat.setReadAvgScore(BigDecimal.ZERO);
                        stat.setReadPassRate("0");
                    } else {
                        // 话术跟读平均最高分
                        final BigDecimal[] readMaxScoresAvg = {BigDecimal.ZERO};
                        AtomicInteger readMaxScoresAvgCount = new AtomicInteger(0);
                        Map<Long, List<StatLessonGroupTraineeDTO>> readingGrouped = readingProjects.stream().collect(Collectors.groupingBy(StatLessonGroupTraineeDTO::getUserId));
                        readingGrouped.forEach((userId, readingStuProjects) -> {
                            BigDecimal maxReadScore = readingStuProjects.stream()
                                    .map(StatLessonGroupTraineeDTO::getComprehensiveScore).filter(Objects::nonNull)
                                    .max(BigDecimal::compareTo)
                                    .orElse(BigDecimal.ZERO);
                            if (maxReadScore.compareTo(BigDecimal.ZERO) > 0){
                                readMaxScoresAvg[0] = readMaxScoresAvg[0].add(maxReadScore);
                                readMaxScoresAvgCount.getAndSet(readMaxScoresAvgCount.get() + 1);
                            }
                        });
                        if (readMaxScoresAvgCount.get() == 0){
                            stat.setReadMaxScore(BigDecimal.ZERO);
                        } else {
                            stat.setReadMaxScore(readMaxScoresAvg[0].divide(new BigDecimal(readMaxScoresAvgCount.get()), 1, RoundingMode.HALF_UP));
                        }

                        // 筛选出脱稿演练练习项目
                        List<StatLessonGroupTraineeDTO> practiceProjects = groupDtos.stream()
                                .filter(x -> ProjectTypeEnum.PRACTICE.getCode().equals(x.getExerciseType()))
                                .collect(Collectors.toList());
                        final BigDecimal[] practiceMaxScoresAvg = {BigDecimal.ZERO};
                        AtomicInteger practiceMaxScoresAvgCount = new AtomicInteger(0);
                        Map<Long, List<StatLessonGroupTraineeDTO>> practiceGrouped = practiceProjects.stream().collect(Collectors.groupingBy(StatLessonGroupTraineeDTO::getUserId));
                        practiceGrouped.forEach((userId, readingStuProjects) -> {
                            BigDecimal maxReadScore = readingStuProjects.stream()
                                    .map(StatLessonGroupTraineeDTO::getComprehensiveScore).filter(Objects::nonNull)
                                    .max(BigDecimal::compareTo)
                                    .orElse(BigDecimal.ZERO);
                            if (maxReadScore.compareTo(BigDecimal.ZERO) > 0){
                                practiceMaxScoresAvg[0] = practiceMaxScoresAvg[0].add(maxReadScore);
                                practiceMaxScoresAvgCount.getAndSet(practiceMaxScoresAvgCount.get() + 1);
                            }
                        });
                        if (practiceMaxScoresAvgCount.get() == 0){
                            stat.setNobookMaxScore(BigDecimal.ZERO);
                        } else {
                            stat.setNobookMaxScore(practiceMaxScoresAvg[0].divide(new BigDecimal(practiceMaxScoresAvgCount.get()), 1, RoundingMode.HALF_UP));
                        }

                        // 筛选出模拟对话练习项目
                        List<StatLessonGroupTraineeDTO> simulatedProjects = groupDtos.stream()
                                .filter(x -> ProjectTypeEnum.SIMULATED.getCode().equals(x.getExerciseType()))
                                .collect(Collectors.toList());
                        final BigDecimal[] simulatedMaxScoresAvg = {BigDecimal.ZERO};
                        AtomicInteger simulatedMaxScoresAvgCount = new AtomicInteger(0);
                        Map<Long, List<StatLessonGroupTraineeDTO>> simulatedGrouped = simulatedProjects.stream().collect(Collectors.groupingBy(StatLessonGroupTraineeDTO::getUserId));
                        simulatedGrouped.forEach((userId, readingStuProjects) -> {
                            BigDecimal maxReadScore = readingStuProjects.stream()
                                    .map(StatLessonGroupTraineeDTO::getComprehensiveScore).filter(Objects::nonNull)
                                    .max(BigDecimal::compareTo)
                                    .orElse(BigDecimal.ZERO);
                            if (maxReadScore.compareTo(BigDecimal.ZERO) > 0){
                                simulatedMaxScoresAvg[0] = simulatedMaxScoresAvg[0].add(maxReadScore);
                                simulatedMaxScoresAvgCount.getAndSet(simulatedMaxScoresAvgCount.get() + 1);
                            }
                        });
                        if (simulatedMaxScoresAvgCount.get() == 0){
                            stat.setDialogueMaxScore(BigDecimal.ZERO);
                        } else {
                            stat.setDialogueMaxScore(simulatedMaxScoresAvg[0].divide(new BigDecimal(simulatedMaxScoresAvgCount.get()), 1, RoundingMode.HALF_UP));
                        }

                        int projectNum = 1 + (practiceMaxScoresAvgCount.get() > 0 ? 1 : 0) + (simulatedMaxScoresAvgCount.get() > 0 ? 1 : 0);
                        BigDecimal lessonScore = stat.getReadMaxScore().add(stat.getDialogueMaxScore()).add(stat.getNobookMaxScore()).divide(BigDecimal.valueOf(projectNum), 1, RoundingMode.HALF_UP);
                        stat.setOverallAvgScore(lessonScore);
                        // 话术跟读平均分
                        BigDecimal totalScoreReading = readingProjects.stream()
                                .map(StatLessonGroupTraineeDTO::getComprehensiveScore).filter(Objects::nonNull)
                                .reduce(BigDecimal.ZERO, BigDecimal::add);
                        stat.setReadAvgScore(
                                totalScoreReading.divide(new BigDecimal(readingCount), 1, RoundingMode.HALF_UP)
                        );

                        // 话术跟读及格率
                        long passCount = readingProjects.stream()
                                .map(StatLessonGroupTraineeDTO::getPassingState)
                                .filter(state -> Integer.valueOf(1).equals(state))
                                .count();
                        BigDecimal passRate = new BigDecimal(passCount)
                                .divide(new BigDecimal(readingCount), 3, RoundingMode.HALF_UP)
                                .multiply(BigDecimal.valueOf(100))
                                .setScale(3, RoundingMode.HALF_UP);
                        if (passRate.compareTo(BigDecimal.ZERO) == 0){
                            stat.setReadPassRate("0");
                        } else if (passRate.compareTo(BigDecimal.valueOf(100)) == 0){
                            stat.setReadPassRate("100");
                        } else {
                            stat.setReadPassRate(passRate.toPlainString().replace(".000", ""));
                        }
                    }
                    stats.add(stat);
                }
            }
        }

        if (CollUtil.isNotEmpty(stats)) {
            saveBatch(stats);
        }
    }

}
